#include "pch.h"
#include "Effects.h"
#include "DXOthers/d3dUtil.h"
#include "EffectHelper.h"	// 必须晚于Effects.h和d3dUtil.h包含
#include "DXOthers/DXTrace.h"
#include "RenderStates.h"
#include <array>
#include <GDX11Renderer\GDxTexture.h>
#include <DXOthers\d3dUtil.cpp>
#include <GGenericInfra/GPtr.h>

using namespace std;
using namespace DirectX;


//
// BasicEffect::Impl 需要先于BasicEffect的定义
//

class BasicEffect::Impl : public AlignedType<BasicEffect::Impl>
{
public:

	//
	// 这些结构体对应HLSL的结构体。需要按16字节对齐
	//

	struct CBPerObj
	{
		DirectX::XMMATRIX world;
		DirectX::XMMATRIX worldInvTranspose;
		Material g_Material;
		float id[4];
	};
	struct CBPerFrame
	{
		DirectX::XMMATRIX view;
		DirectX::XMMATRIX proj;
		XMFLOAT3 camPos;
		float pad;
	};

	struct CBMAT
	{
		Material mat;
	};

	struct CBLightInfo
	{
// 		PointLight points[MAX_POINT_LIGHT_NUM];
// 		DirectionalLight light[MAX_POINT_LIGHT_NUM];
// 		int pointCount;
// 		int lightCount;
// 		int pad[2];
		DirectionalLight g_DirLight;
		PointLight g_PointLight[MAX_POINT_LIGHT_NUM];
		SpotLight g_SpotLight;
		int pointCount;
		float g_Pad[3];
	};

public:
	// 必须显式指定
	Impl() : m_IsDirty() {}
	~Impl() = default;

public:
	// 需要16字节对齐的优先放在前面
	CBufferObject<0, CBPerObj>			mCBPerObj;	
 	CBufferObject<1, CBPerFrame>		mCBPerFrame;
	CBufferObject<2, CBLightInfo>		mCBLightInfo;
	//CBufferObject<4, CBMAT>		mCBmat;

	BOOL m_IsDirty;											    // 是否有值变更
	std::vector<CBufferBase*> m_pCBuffers;					    // 统一管理上面所有的常量缓冲区

	ComPtr<ID3D11VertexShader> m_pDeferredVS;
	ComPtr<ID3D11PixelShader> m_pDeferredPS;

	ComPtr<ID3D11VertexShader> m_pBasicVS;
	ComPtr<ID3D11PixelShader> m_pBasicPS;

	ComPtr<ID3D11InputLayout> m_pVertexLayout;			// 3D顶点输入布局

	ComPtr<ID3D11ShaderResourceView> mDiffuse;		    // albedo out() or albedo in 紋理
	ComPtr<ID3D11ShaderResourceView> mBump;				// normal out or normal in 紋理
	ComPtr<ID3D11ShaderResourceView> mWorldPos;		    // worldPos in 紋理
	ComPtr<ID3D11ShaderResourceView> mAORoughnessMetal;	// AO Roughness Metal out or in 紋理
};

//
// BasicEffect
//

namespace
{
	// BasicEffect单例
	static BasicEffect * g_pInstance = nullptr;
}

BasicEffect::BasicEffect()
{
	if (g_pInstance)
		throw std::exception("BasicEffect is a singleton!");
	g_pInstance = this;
	pImpl = std::make_unique<BasicEffect::Impl>();
}

BasicEffect::~BasicEffect()
{
}

BasicEffect::BasicEffect(BasicEffect && moveFrom) noexcept
{
	pImpl.swap(moveFrom.pImpl);
}

BasicEffect & BasicEffect::operator=(BasicEffect && moveFrom) noexcept
{
	pImpl.swap(moveFrom.pImpl);
	return *this;
}

BasicEffect & BasicEffect::Get()
{
	if (!g_pInstance)
	{
		new BasicEffect();
	}
		
	return *g_pInstance;
}

bool BasicEffect::InitAll(ID3D11Device* device)
{
	if (!device)
		return false;


	if (!pImpl->m_pCBuffers.empty())
		return true;

	if (!RenderStates::IsInit())
	{
		RenderStates::InitAll(device);
	}

	ComPtr<ID3DBlob> blob;

	// 创建顶点着色器
	HR(CreateShaderFromFile(L"..\\GDX11Renderer\\HLSL\\Debug\\DeferredVS.cso", L"..\\GDX11Renderer\\HLSL\\DeferredVS.hlsl", "VS", "vs_5_0", blob.ReleaseAndGetAddressOf()));
	HR(device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, pImpl->m_pDeferredVS.GetAddressOf()));
	
	HR(CreateShaderFromFile(L"..\\GDX11Renderer\\HLSL\\Debug\\Basic_VS.cso", L"..\\GDX11Renderer\\HLSL\\Basic_VS.hlsl", "VS", "vs_5_0", blob.ReleaseAndGetAddressOf()));
	HR(device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, pImpl->m_pBasicVS.GetAddressOf()));

	// 创建顶点布局
	HR(device->CreateInputLayout(GDxVertex::VertexPosNormalTangentTex, ARRAYSIZE(GDxVertex::VertexPosNormalTangentTex),
		blob->GetBufferPointer(), blob->GetBufferSize(), pImpl->m_pVertexLayout.GetAddressOf()));

	// 创建像素着色器
	HR(CreateShaderFromFile(L"..\\GDX11Renderer\\HLSL\\Debug\\DeferredPS.cso", L"..\\GDX11Renderer\\HLSL\\DeferredPS.hlsl", "PS", "ps_5_0", blob.ReleaseAndGetAddressOf()));
	HR(device->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, pImpl->m_pDeferredPS.GetAddressOf()));

	HR(CreateShaderFromFile(L"..\\GDX11Renderer\\HLSL\\Debug\\Basic_PS.cso", L"..\\GDX11Renderer\\HLSL\\Basic_PS.hlsl", "PS", "ps_5_0", blob.ReleaseAndGetAddressOf()));
	HR(device->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, pImpl->m_pBasicPS.GetAddressOf()));

	pImpl->m_pCBuffers.assign({
		&pImpl->mCBPerObj,
		&pImpl->mCBPerFrame,
		&pImpl->mCBLightInfo });

	// 创建常量缓冲区
	for (auto& pBuffer : pImpl->m_pCBuffers)
	{
		HR(pBuffer->CreateBuffer(device));
	}

	// 设置调试对象名
	D3D11SetDebugObjectName(pImpl->m_pVertexLayout.Get(), "VertexPosNormalTangentTex");
	D3D11SetDebugObjectName(pImpl->m_pCBuffers[0]->cBuffer.Get(), "CBPerObj");
	D3D11SetDebugObjectName(pImpl->m_pCBuffers[1]->cBuffer.Get(), "CBPerFrame");
	D3D11SetDebugObjectName(pImpl->m_pCBuffers[2]->cBuffer.Get(), "CBLightInfo");
	D3D11SetDebugObjectName(pImpl->m_pBasicVS.Get(), "Basic_VS");
	D3D11SetDebugObjectName(pImpl->m_pBasicPS.Get(), "Basic_PS");
	D3D11SetDebugObjectName(pImpl->m_pDeferredVS.Get(), "DeferredVS");
	D3D11SetDebugObjectName(pImpl->m_pDeferredPS.Get(), "DeferredPS");

	return true;
}

void BasicEffect::SetRenderDefault(ID3D11DeviceContext * deviceContext)
{
	deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	deviceContext->IASetInputLayout(pImpl->m_pVertexLayout.Get());
	deviceContext->VSSetShader(pImpl->m_pBasicVS.Get(), nullptr, 0);
	deviceContext->GSSetShader(nullptr, nullptr, 0);
	deviceContext->RSSetState(nullptr);
	deviceContext->PSSetShader(pImpl->m_pBasicPS.Get(), nullptr, 0);
	deviceContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf());
	//deviceContext->OMSetDepthStencilState(nullptr, 0);
	deviceContext->OMSetDepthStencilState(RenderStates::DSSLessEqual.Get(), 0);
	deviceContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF);
}

void BasicEffect::SetGBufferPass(ID3D11DeviceContext* deviceContext)
{
	deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	deviceContext->IASetInputLayout(pImpl->m_pVertexLayout.Get());
	deviceContext->VSSetShader(pImpl->m_pDeferredVS.Get(), nullptr, 0);
	deviceContext->GSSetShader(nullptr, nullptr, 0);
	deviceContext->RSSetState(nullptr);
	deviceContext->PSSetShader(pImpl->m_pDeferredPS.Get(), nullptr, 0);
	deviceContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf());
	deviceContext->OMSetDepthStencilState(nullptr, 0);
	deviceContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF);
}



void BasicEffect::SetId(int id)
{
	auto& cBuffer = pImpl->mCBPerObj;
	cBuffer.data.id[1] = id ;
	pImpl->m_IsDirty = cBuffer.isDirty = true;
}
void BasicEffect::SetFlag(int flag)
{
	auto& cBuffer = pImpl->mCBPerObj;
	cBuffer.data.id[0] = flag;
	pImpl->m_IsDirty = cBuffer.isDirty = true;
}

void XM_CALLCONV BasicEffect::SetWorldMatrix(std::array<float, 16> W)
{
	XMFLOAT4X4 test;

	float* temp = (float*)test.m;
	for (size_t i = 0; i < 16; i++)
	{
		*temp = W[i];
		temp++;
	}
	return SetWorldMatrix(XMLoadFloat4x4(&test));
}

void XM_CALLCONV BasicEffect::SetWorldMatrix(DirectX::FXMMATRIX W)
{
	auto& cBuffer = pImpl->mCBPerObj;
	cBuffer.data.world = XMMatrixTranspose(W);
	cBuffer.data.worldInvTranspose = XMMatrixInverse(nullptr, W);
	pImpl->m_IsDirty = cBuffer.isDirty = true;
}

void XM_CALLCONV BasicEffect::SetViewMatrix(FXMMATRIX V)
{
	
	auto& cBuffer = pImpl->mCBPerFrame;
	cBuffer.data.view = XMMatrixTranspose(V);
	cBuffer.data.camPos.x = cBuffer.data.view.r[0].m128_f32[3];
	cBuffer.data.camPos.y = cBuffer.data.view.r[1].m128_f32[3];
	cBuffer.data.camPos.z = cBuffer.data.view.r[2].m128_f32[3];
	pImpl->m_IsDirty = cBuffer.isDirty = true;
}

void XM_CALLCONV BasicEffect::SetProjMatrix(FXMMATRIX P)
{
	auto& cBuffer = pImpl->mCBPerFrame;
	cBuffer.data.proj = XMMatrixTranspose(P);
	pImpl->m_IsDirty = cBuffer.isDirty = true;
}



void BasicEffect::SetLight(size_t pos, GUSPtr<GRiObj> pLight)
{
	auto cmpLight = pLight->GetComponent<GRiCmpLight>();
	auto light = dynamic_cast<GUSPtr<GRiPointLight>>(cmpLight->pLight.get());

	if (light != nullptr)
	{
		PointLight l;
		l.Color = light->Color;
		l.Intensity = light->Intensity;
		l.Position = pLight->GetComponent<GRiCmpTransform>()->GetLocation();
		l.Range = light->Range;
		SetPointLight(pos, l);
	}
}

void BasicEffect::SetDirLight(size_t pos, const DirectionalLight & dirLight)
 {
	 auto& cBuffer = pImpl->mCBLightInfo;
	 cBuffer.data.g_DirLight = dirLight;
	 //cBuffer.data.lightCount = max(cBuffer.data.lightCount, pos + 1);
	 pImpl->m_IsDirty = cBuffer.isDirty = true;
 }
 
	void BasicEffect::SetPointLight(size_t pos, const PointLight & pointLight)
	{
		auto& cBuffer = pImpl->mCBLightInfo;
		cBuffer.data.g_PointLight[pos] = pointLight;
		cBuffer.data.pointCount = max(cBuffer.data.pointCount, pos + 1);
		pImpl->m_IsDirty = cBuffer.isDirty = true;
	}
	
// 
// void BasicEffect::SetSpotLight(size_t pos, const SpotLight & spotLight)
// {
// 	auto& cBuffer = pImpl->m_CBRarely;
// 	cBuffer.data.spotLight[pos] = spotLight;
// 	pImpl->m_IsDirty = cBuffer.isDirty = true;
// }
// 
 void BasicEffect::SetMaterial(const Material & material)
 {
	 auto& cBuffer = pImpl->mCBPerObj;
	 cBuffer.data.g_Material = material;
	 ///cBuffer.data.pointCount = max(cBuffer.data.pointCount, pos + 1);
	 pImpl->m_IsDirty = cBuffer.isDirty = true;
 }

//void XM_CALLCONV BasicEffect::SetEyePos(FXMVECTOR eyePos)
//{
//	auto& cBuffer = pImpl->m_CBFrame;
//	cBuffer.data.eyePos = eyePos;
//	pImpl->m_IsDirty = cBuffer.isDirty = true;
//}

void BasicEffect::SetTextureDiffuse(ID3D11ShaderResourceView* textureDiffuse)
{
	pImpl->mDiffuse = textureDiffuse;
}
void BasicEffect::SetTextureBump(ID3D11ShaderResourceView* textureBump)
{
	pImpl->mBump = textureBump;
}
void BasicEffect::SetTextureORM(ID3D11ShaderResourceView* textureOrm)
{
	pImpl->mAORoughnessMetal = textureOrm;
}
void BasicEffect::SetTexturesMainPass(ID3D11ShaderResourceView** textures)
{
	pImpl->mDiffuse = textures[0];
	pImpl->mBump = textures[1];
	pImpl->mWorldPos = textures[2];
	pImpl->mAORoughnessMetal = textures[3];
}

void BasicEffect::Apply(ID3D11DeviceContext * deviceContext)
{
	auto& pCBuffers = pImpl->m_pCBuffers;
	// 将缓冲区绑定到渲染管线上
	pCBuffers[0]->BindVS(deviceContext);
	pCBuffers[1]->BindVS(deviceContext);
	pCBuffers[2]->BindVS(deviceContext);
	//
	pCBuffers[0]->BindPS(deviceContext);
	pCBuffers[1]->BindPS(deviceContext);
	pCBuffers[2]->BindPS(deviceContext);
	// pCBuffers[3]->BindPS(deviceContext);
	// 设置纹理

	deviceContext->PSSetShaderResources(0, 1, pImpl->mDiffuse.GetAddressOf());
	deviceContext->PSSetShaderResources(1, 1, pImpl->mBump.GetAddressOf());	
	deviceContext->PSSetShaderResources(2, 1, pImpl->mWorldPos.GetAddressOf());
	deviceContext->PSSetShaderResources(3, 1, pImpl->mAORoughnessMetal.GetAddressOf());

	if (pImpl->m_IsDirty)
	{
		pImpl->m_IsDirty = false;
		for (auto& pCBuffer : pCBuffers)
		{
			pCBuffer->UpdateBuffer(deviceContext);
		}
	}
}
